﻿using System;
using System.Reflection;
using System.Linq;
using System.Text;

using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;

using System.Windows;
using System.Windows.Data;
using System.Windows.Threading;
using System.Windows.Controls;

using Caliburn.Micro;

using GTServices.Entities;
using GTServices.Model;
using GTServices.WPF.Client.ViewModels.Entities;
using GTServices.WPF.Client.Extensions;
using GTServices.Model.Extensions;
using GTServices.Model.Models;
using GTServices.WPF.Client.Data;

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Data.Objects;
using System.Xml;
using System.Xml.Serialization;


//THe documents and Anchorables for displaying. 

namespace GTServices.WPF.Client.ViewModels
{
    #region TreeViewItem Defintions.
    public class TreeViewItemViewModel : PropertyChangedBase
    {
        private bool isExpanded; 
        public bool IsExpanded
        {
            get { return isExpanded; }
            set
            {
                if (isExpanded == false && value == true)
                {
                    RefreshChildren(); 
                }
                isExpanded = value; 
                NotifyOfPropertyChange(() => IsExpanded); 
            }
        }

        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                isSelected = value;
                NotifyOfPropertyChange(() => IsSelected);
            }
        }       

        public virtual TreeViewItemViewModel Parent { get; set; }
        public ListCollectionView Children { get; set; }



        public TreeViewItemViewModel(TreeViewItemViewModel parent, System.Collections.IList _children)
        {
            this.Parent = parent;
            this.Children = new ListCollectionView(_children); 
        }
        public TreeViewItemViewModel(TreeViewItemViewModel parent, ListCollectionView _children)
        {
            this.Parent = parent;
            this.Children = _children;
        }
        public TreeViewItemViewModel(TreeViewItemViewModel parent)
        {
            this.Parent = parent;
        }
        protected TreeViewItemViewModel()
        {

        }
        public virtual void RefreshChildren()
        {
            this.Children.Refresh();
        }
    }
    public class CategoryTreeViewItemViewModel : TreeViewItemViewModel
    {
        private Category _category;
        public Category Category
        {
            get
            {
                return _category;
            }
            set
            {
                _category = value;
                RefreshChildren();
                NotifyOfPropertyChange(() => Category); 
            }
        }

        public CategoryTreeViewItemViewModel()
        {

        }

        public override void RefreshChildren()
        {
            if (this.Parent != null && this.Parent is GrepResultTreeViewItemViewModel)
            {
                GrepResult gr = (Parent as GrepResultTreeViewItemViewModel).GrepResult;
                if (gr == null)
                {
                    //This shouldn't happen?
                    throw new ArgumentException("This shouldn't happen"); 
                }
                ListCollectionView lcv = new ListCollectionView(new BindableCollection<MatchInfoViewModel>());
                gr.MatchInfos.Apply(x => { 
                    if(Category.Patterns.Contains(x.Pattern)){

                        //I think I hit a deadlock around this code. I need to see if this is an issue. 
                        lcv.AddNewItem(new MatchInfoViewModel() { MatchInfo = x });
                    }
                });
                lcv.CommitNew();
                lcv.Refresh();
                Children = lcv;
                NotifyOfPropertyChange(() => Children);
            }
        }

        void x_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.Children.Refresh(); 
        }
    }
    public class GrepResultTreeViewItemViewModel : TreeViewItemViewModel
    {
        private GrepResult _grepResult;
        public GrepResult GrepResult
        {
            get { return _grepResult; }
            set
            {
                _grepResult = value;
               
                RefreshChildren(); 
                NotifyOfPropertyChange(() => GrepResult);
                NotifyOfPropertyChange(() => Children); 
            }
        }


        public BindableCollection<CategoryTreeViewItemViewModel> _children;

        public int TotalMatchInfos { get { return GrepResult.MatchInfos.Count();  } }


        public GrepResultTreeViewItemViewModel()
        {

        }

        public override void RefreshChildren()
        {
            this.Children = new ListCollectionView(new List<CategoryTreeViewItemViewModel>());

            this.Children.Filter += new Predicate<object>(x =>
            {
                if (x is CategoryTreeViewItemViewModel)
                {
                    CategoryTreeViewItemViewModel itv = x as CategoryTreeViewItemViewModel;
                    if (itv.Children.Count > 0)
                    {
                        return true;
                    }
                }
                return false;
            }); 

            var categories = this.GrepResult.GrepRequest.Categories;
            foreach (Category c in categories)
            {
  
                CategoryTreeViewItemViewModel itv = new CategoryTreeViewItemViewModel()
                {   
                    Parent = this,
                    Category = c
                   // Children = new ListCollectionView(matches),
                };
                this.Children.AddNewItem(itv);
                this.Children.CommitNew(); 
            }
            NotifyOfPropertyChange(() => Children);
            NotifyOfPropertyChange(() => TotalMatchInfos);
        }
    }
    #endregion

    /// <summary>
    /// This is the core results view. This will also handle all events from the children. Including the TreeViewItem's handlers for things like Remove, Show Result, etc. 
    /// </summary>
    public class GrepResultsTreeViewModel : TreeViewItemViewModel
    {
        private SyncedBindableCollection<GrepResult, GrepResultTreeViewItemViewModel> _rootGrepResults;

        public SyncedBindableCollection<GrepResult, GrepResultTreeViewItemViewModel> RootGrepResults {
            get{ return _rootGrepResults; }
            set
            {
                _rootGrepResults = value;
                NotifyOfPropertyChange(() => RootGrepResults);
            }
        } 

        public List<Object> SelectedItems { get; set; } 
        public MatchInfoViewModel SelectedMatchInfo { get; set; }
        
        //StateStuffs
        private bool _hideReviewed;
        public bool HideReviewed
        {
            get { return _hideReviewed; }
            set
            {
                this._hideReviewed = value;
                RootGrepResults.Apply(x =>{
                    
                     var childs  = x.Children.OfType<CategoryTreeViewItemViewModel>();
                     childs.Apply(y => y.RefreshChildren());
                        
                });
                NotifyOfPropertyChange(() => HideReviewed);
            }
        }
        public String MatchInfoFilter { get; set; }

        public MatchInfoDetailViewModel MatchInfoDetailViewModel { get; set; }
        public MatchInfoEditorViewModel MatchInfoEditorViewModel { get; set; }

        private object _previousSelectedItemSource; 
        
        public GrepResultsTreeViewModel()
        {
            this.SelectedItems = new List<object>();
            this.RootGrepResults = new SyncedBindableCollection<GrepResult, GrepResultTreeViewItemViewModel>(GTApplication.Instance.DataModel.GrepResults,
                                                                                                            (x) => new GrepResultTreeViewItemViewModel() { GrepResult = x, Parent = this },
                                                                                                            (m, vm) => vm.GrepResult.Equals(m));

            MatchInfoEditorViewModel = new MatchInfoEditorViewModel();

            NotifyOfPropertyChange(() => MatchInfoEditorViewModel);
            _previousSelectedItemSource = null;
        }

        public GrepResultsTreeViewModel(SyncedBindableCollection<GrepResult, GrepResultTreeViewItemViewModel> _results)
        {

            MatchInfoEditorViewModel = new MatchInfoEditorViewModel();


            this.SelectedItems = new List<object>();
            this.RootGrepResults = _results;
        }

        #region Action Handlers


        public void SetRootPath(ActionExecutionContext context)
        {

            GrepResult gr = null;
            MenuItem mi = context.Source as MenuItem;
            if (mi == null)
                return;

            if (mi.Tag is GrepResultTreeViewItemViewModel)
            {
                GrepResultTreeViewItemViewModel grtvi = mi.Tag as GrepResultTreeViewItemViewModel;
                gr = grtvi.GrepResult;
                if (gr != null)
                {
                    System.Windows.Forms.FolderBrowserDialog fbd = new System.Windows.Forms.FolderBrowserDialog();

                    if (System.IO.Directory.Exists(gr.GrepRequest.RootPath))
                        fbd.SelectedPath = gr.GrepRequest.RootPath;
                    
                    if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        string ogRootPath = gr.GrepRequest.RootPath;

                        gr.GrepRequest.RootPath = fbd.SelectedPath;

                        foreach (GTServices.Entities.FileInfo fi in gr.GrepRequest.FileInfos)
                        {
                            fi.Path = fi.Path.Remove(0, ogRootPath.Length);
                            fi.Path = fbd.SelectedPath + fi.Path;
                        }
                    }
                }
            }
        }

        public void RemoveGrepResult(ActionExecutionContext context)
        {

            GrepResult gr = null;
            MenuItem mi = context.Source as MenuItem;
            if (mi == null)
                return;

            if (mi.Tag is GrepResultTreeViewItemViewModel)
            {
                GrepResultTreeViewItemViewModel grtvi = mi.Tag as GrepResultTreeViewItemViewModel;
                gr = grtvi.GrepResult;
                if (gr != null)
                {
                    GTApplication.Instance.DataModel.Remove(gr); 
                }
            }
        }

        public void RemoveCategoryFromGrepResult(ActionExecutionContext context)
        {
            MenuItem mi = context.Source as MenuItem;
            if (mi == null)
                return;

            if (mi.Tag is CategoryTreeViewItemViewModel)
            {

                 CategoryTreeViewItemViewModel tvitem = mi.Tag as CategoryTreeViewItemViewModel; 

                 //Need to refresh the children of the appropriate SearchResultTreeViewModel. 
                 GrepResultTreeViewItemViewModel parent = tvitem.Parent as GrepResultTreeViewItemViewModel;
                 if (parent != null)
                 {
                     var itemsToRemove = parent.GrepResult.MatchInfos.Where(x =>
                     {
                         return tvitem.Children.Contains(x);
                     }).ToList();
                     foreach (MatchInfo item in itemsToRemove)
                     {
                         parent.GrepResult.MatchInfos.Remove(item);
                     }
                     parent.Children.Remove(tvitem);
                     parent.RefreshChildren(); 
                    
                 }
            }
        }
        public void GetAsXml(ActionExecutionContext context)
        {
            string ret = "";
            MatchInfoViewModel mi = this.SelectedItems.FirstOrDefault() as MatchInfoViewModel;

            try
            {
                var serializer = new DataContractSerializer(typeof(MatchInfo), typeof(MatchInfo).Assembly.GetTypes(), 10000, false, true, null, new ProxyDataContractResolver()); 

                using (var stream = new MemoryStream())
                {
                    // This will also lazy load all details
                    serializer.WriteObject(stream, mi.MatchInfo);
                    stream.Seek(0, SeekOrigin.Begin);
                    StreamReader sr = new StreamReader(stream);
                    string s = sr.ReadToEnd();
                    Clipboard.SetText(s);
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        public void ToBug(ActionExecutionContext context)
        {
            //First we need to get the selected matchInfos. 

            var matchInfos = this.SelectedItems.Cast<MatchInfoViewModel>().ToList(); 
           
            if (matchInfos.Count() > 0)
            {
                //They should all originate from the same GrepRequest. 
                GrepRequest gr = matchInfos[0].MatchInfo.GrepRequest;

                Bug b = new Bug();
                b.MatchInfos = matchInfos.Select(x => x.MatchInfo).ToList();

                //To resolve the issue, we need to look at the GrepRequest.

                Category c = gr.Categories.FirstOrDefault(x => x.Patterns.Contains(matchInfos[0].MatchInfo.Pattern));
                if (c != null)
                {
                    b.Category = c;
                    b.Template = c.Template;
                }

                CreateBugViewModel cbvm = new CreateBugViewModel()
                {
                   Bug = b
                };

                /*
                if (GTWindowManager.Instance.ShowDialog(cbvm, 500, 500) == true)
                {
                    //Log if we want. or something doesnt really matter. 
                    GTApplication.Instance.DataModel.Bugs.Add(b);
                }*/

                GTWindowManager.Instance.ShowWindow(cbvm, 700, 1200); 
            }
            else
            {
                //TODO: Error? Throw?
                return;
            }
        }

        public void RemoveMatchInfoAction(ActionExecutionContext context)
        {
            MenuItem mi = context.Source as MenuItem;
            if (mi == null)
                return;

            if (mi.Tag is CategoryTreeViewItemViewModel)
            {
                CategoryTreeViewItemViewModel isr = mi.Tag as CategoryTreeViewItemViewModel; 
                if (this.SelectedItems != null && this.SelectedItems.Count > 0)
                {
                    //Need to refresh the children of the appropriate SearchResultTreeViewModel. 

                    if (isr != null)   
                    {
                        var parent = isr.Parent as GrepResultTreeViewItemViewModel;
                        if (parent != null)
                        {
                            foreach (MatchInfo match in SelectedItems.Cast<MatchInfoViewModel>().Select(x=>x.MatchInfo).ToList())
                            {
                                parent.GrepResult.MatchInfos.Remove(match); 
                            }
                            isr.RefreshChildren();
                        }
                    }
                }
            }
        }


        public void SortClick(ActionExecutionContext context)
        {
            Button b = context.Source as Button;

            ListCollectionView lcv = (b.Tag as ListView).ItemsSource as ListCollectionView;
            if (b == null || lcv == null)
            {
                //TODO: Log + Error
                return;
            }

            ListSortDirection sortDir = ListSortDirection.Ascending;
            if (lcv.SortDescriptions.Count > 0)
            {
                sortDir = lcv.SortDescriptions[0].Direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
            }

            switch (b.Content.ToString())
            {
    
                case "File":
                    lcv.SortDescriptions.Clear();
                    lcv.SortDescriptions.Add(new System.ComponentModel.SortDescription("MatchInfo.FileInfo.Name", sortDir));
                    break;
                case "Line#":
                    lcv.SortDescriptions.Clear();
                    lcv.SortDescriptions.Add(new System.ComponentModel.SortDescription("MatchInfo.LineNumber", sortDir));
                    break;
                case "Pattern":
                    lcv.SortDescriptions.Clear();
                    lcv.SortDescriptions.Add(new System.ComponentModel.SortDescription("MatchInfo.Pattern.PatternStr", sortDir));
                    break;
                case "Context":

                    lcv.SortDescriptions.Clear();
                    lcv.SortDescriptions.Add(new System.ComponentModel.SortDescription("MatchInfo.Line", sortDir));
                    break;
                case "Fulle Path":

                    lcv.SortDescriptions.Clear();
                    lcv.SortDescriptions.Add(new System.ComponentModel.SortDescription("MatchInfo.FileInfo.FullName", sortDir));
                    break;
                default:
                    break;
            }
            return;
        }
        
      

        public void MarkReviewed(ActionExecutionContext context)
        {
            if (this.SelectedItems != null && this.SelectedItems.Count > 0)
            {
               
                var items = this.SelectedItems.Cast<MatchInfoViewModel>().ToList();
                foreach (MatchInfoViewModel mi in items)
                {
                    mi.Reviewed = !mi.Reviewed;
                }
            }
        }
   
        public void ExportResult(ActionExecutionContext context)
        {
            GrepResult gr = null;
            MenuItem mi = context.Source as MenuItem;
            if (mi == null)
                return;

            if (mi.Tag is GrepResultTreeViewItemViewModel)
            {
                GrepResultTreeViewItemViewModel grtvi = mi.Tag as GrepResultTreeViewItemViewModel;
                gr = grtvi.GrepResult; 
            }
            if (gr != null)
            {

                Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
                sfd.DefaultExt = ".xml"; // Default file extension
                sfd.Filter = "Xml documents (.xml)|*.xml"; // Filter files by extension 
                if (sfd.ShowDialog() == true)
                {
                    try
                    {
                        using (StreamWriter sw = new StreamWriter(File.Open(sfd.FileName, FileMode.Create)))
                        {
                            sw.Write(GTApplication.Instance.DataModel.GetEntityAsXml(gr));
                        }
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.Message);
                    }
                }
            }
            else
            {
                MessageBox.Show("Selected Grep Result is null?!?!.. Try again???"); 
            }
        }
        public void ImportResults()
        {

            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.DefaultExt = ".xml"; // Default file extension
            dlg.Filter = "Xml Documents (.xml)|*.xml"; // Filter files by extension 
            dlg.Multiselect = true;

            if (dlg.ShowDialog() == true)
            {
                foreach (String fileName in dlg.FileNames)
                {
                    if (System.IO.File.Exists(fileName))
                    {
                        System.IO.StreamReader sr = new System.IO.StreamReader(System.IO.File.Open(fileName, System.IO.FileMode.Open));
                        string contents = sr.ReadToEnd();
                        try
                        {
                            Object o = GTApplication.Instance.DataModel.DeserializeXml(contents);
                            if (o is GrepResult && o != null)
                            {
                                GTApplication.Instance.DataModel.GrepResults.Add(o as GrepResult); 
                            }
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show(e.Message);
                        }
                    }
                }
            }
        }
     
        public void ShowResultInWin(ActionExecutionContext context)
        {
            if (this.SelectedMatchInfo != null)
            {
                List<CategoryTreeViewItemViewModel> itvi = RootGrepResults.SelectMany(x => x.Children.OfType<CategoryTreeViewItemViewModel>()).ToList();
                List<MatchInfoViewModel> mInfos = itvi.SelectMany(x => x.Children.OfType<MatchInfoViewModel>()).ToList();

                if (Properties.Settings.Default.ReuseCodeViewWindow)
                {
                    if (this.MatchInfoDetailViewModel == null)
                    {
                        this.MatchInfoDetailViewModel = new MatchInfoDetailViewModel(); 
                    }
                }
                else
                {
                    this.MatchInfoDetailViewModel = new MatchInfoDetailViewModel(); 
                }

                MatchInfoDetailViewModel.ParentViewModel = this;
                MatchInfoDetailViewModel.FileMatchs =  new BindableCollection<MatchInfoViewModel>(mInfos.Where( x=> x.MatchInfo.FileInfo == SelectedMatchInfo.MatchInfo.FileInfo).ToList());

                if (!MatchInfoDetailViewModel.IsActive)
                {
                    GTWindowManager.Instance.ShowWindow(MatchInfoDetailViewModel, 500, 1000);
                }

                MatchInfoDetailViewModel.MatchInfo = this.SelectedMatchInfo.MatchInfo;
            }
        }
        public void SelectedItemChanged(ActionExecutionContext context)
        {
            SelectionChangedEventArgs eventArgs = context.EventArgs as SelectionChangedEventArgs;
            if (_previousSelectedItemSource == null)
                _previousSelectedItemSource = eventArgs.Source; 

            if (eventArgs != null)
            {
                if (_previousSelectedItemSource != eventArgs.Source)
                {
                    SelectedItems.Clear();
                   
                }
                _previousSelectedItemSource = eventArgs.Source;

                eventArgs.RemovedItems.ForEach((x) =>
                {
                    if(SelectedItems.Contains(x))
                        SelectedItems.Remove(x);
                   
                });

                eventArgs.AddedItems.ForEach((x) =>
                {
                    SelectedItems.Add(x);
                });
                if (SelectedItems.Count > 0)
                {
                    MatchInfoViewModel mi = SelectedItems[0] as MatchInfoViewModel;
                    if (mi != null)
                    {
                        MatchInfoEditorViewModel.MatchInfo = mi.MatchInfo;
                        NotifyOfPropertyChange(() => MatchInfoEditorViewModel);
                    }

                }
           
            }
        }

        public void CopySelectedItemsPath(ActionExecutionContext context)
        {
            if (this.SelectedMatchInfo != null)
            {
                try
                {
                    Clipboard.SetText(this.SelectedMatchInfo.MatchInfo.FileInfo.FullName);
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message); 
                }
            }
        }
        #endregion
        
     
    }
}
